using System;
using System.Data;
using System.Data.Linq;
using RomanJendrusz.Amazon.ETL.Infrastructure.DataBase;
using RomanJendrusz.Amazon.ETL.Domain;
using RomanJendrusz.Amazon.ETL.Infrastructure.IoC;
using System.Collections;
using RomanJendrusz.Amazon.ETL.Infrastructure.Loging.Core;
using System.Linq.Expressions;
using System.Linq;

namespace RomanJendrusz.Amazon.ETL.Repositories
{
    public class AmazonSession: ISession, IUnitOfWork
    {
        private readonly DataContext innerContext;
        private readonly IQuerySource<Customer> customers;
        private readonly IQuerySource<Book> books;
        private readonly IQuerySource<CustomerReview> customersReviews;
        private readonly IQuerySource<ListMania> listManias;
        private readonly IQuerySource<WishList> wishlists;
        private readonly IQuerySource<Seller> sellers;
        private readonly IQuerySource<SellerFeedback> sellerFeedbacks;
        private readonly IQuerySource<SellerOffer> sellerOffers;
        private readonly IQuerySource<SimiliarBook> similiarBooks;
        private readonly IQuerySource<Author> authors;
        private readonly IQuerySource<BookAuthors> bookAuthors;
        private readonly IQuerySource<Domain.BrowseNode> browsNodes;
        private IBookRepository bookRepository;
        private ICustomerRepository customerRepository;
        private ICustomerReviewRepository customerReviewRepository;
        private IAuthorRepository authorRepository;
        private ISellerOffersRepository sellerOffersRepository;
        private IQuerySource<BrowseNodesBook> browsNodesBooks;
        private IBrowseNodeRepository browseNodeRepository;


        public AmazonSession(IMappingSourceBuilder mappingSourceBuilder, IDataBaseConnectionFactory connectionFactory)
        {
            if (mappingSourceBuilder == null) throw new ArgumentNullException("mappingSourceBuilder");
            if (connectionFactory == null) throw new ArgumentNullException("connectionFactory");
            innerContext = new DataContext(connectionFactory.DBConnection, mappingSourceBuilder.MappingSource);
            customers = new TableQuerySource<Customer>(innerContext.GetTable<Customer>());
            books = new TableQuerySource<Book>(innerContext.GetTable<Book>());
            customersReviews = new TableQuerySource<CustomerReview>(innerContext.GetTable<CustomerReview>());
            listManias = new TableQuerySource<ListMania>(innerContext.GetTable<ListMania>());
            wishlists = new DerivedTableQuerySource<WishList, ListMania>(innerContext.GetTable<ListMania>());
            sellers = new TableQuerySource<Seller>(innerContext.GetTable<Seller>());
            sellerFeedbacks = new TableQuerySource<SellerFeedback>(innerContext.GetTable<SellerFeedback>());
            sellerOffers = new TableQuerySource<SellerOffer>(innerContext.GetTable<SellerOffer>());
            similiarBooks = new TableQuerySource<SimiliarBook>(innerContext.GetTable<SimiliarBook>());
            authors = new TableQuerySource<Author>(innerContext.GetTable<Author>());
            bookAuthors = new TableQuerySource<BookAuthors>(innerContext.GetTable<BookAuthors>());
            browsNodes = new TableQuerySource<Domain.BrowseNode>(innerContext.GetTable<BrowseNode>());
            browsNodesBooks = new TableQuerySource<BrowseNodesBook>(innerContext.GetTable<BrowseNodesBook>());
        }


        public void SubmitChanges()
        {
            LogGateway.LogFor(GetType()).Warn("Submiting batch of data");
            innerContext.SubmitChanges();
        }

        public IQuerySource<Customer> Customers
        {
            get { return customers;}
        }

        public IQuerySource<Book> Books
        {
            get { return books; }
        }

        public IQuerySource<CustomerReview> CustomersReviews
        {
            get { return customersReviews; }
        }

        public IQuerySource<ListMania> ListManias
        {
            get { return listManias; }
        }

        public IQuerySource<WishList> Wishlists
        {
            get { return wishlists; }
        }

        public IQuerySource<Seller> Sellers
        {
            get { return sellers;}
        }


        public IQuerySource<SellerFeedback> SellerFeedbacks
        {
            get { return sellerFeedbacks; }
        }

        public IQuerySource<SellerOffer> SellerOffers
        {
            get { return sellerOffers; }
        }

        public IQuerySource<SimiliarBook> SimiliarBooks
        {
            get { return similiarBooks; }
        }

        public IQuerySource<Author> Authors
        {
            get { return authors;}
        }

        public IQuerySource<BookAuthors> BookAuthors
        {
            get { return bookAuthors; }
        }

        public IQuerySource<BrowseNode> BrowsNodes
        {
            get { return browsNodes; }
        }

        public IQuerySource<BrowseNodesBook> BrowsNodesBooks
        {
            get { return browsNodesBooks;  }
        }

        public IBookRepository BookRepository
        {
            get
            {
                if(bookRepository == null)
                {
                    bookRepository = GetRepository<IBookRepository>();
                }
                return bookRepository;
            }
        }

        public ICustomerRepository CustomerRepository
        {
            get
            {
                if (customerRepository == null)
                {
                    customerRepository = GetRepository<ICustomerRepository>();
                }
                return customerRepository;
            }
        }

        public ICustomerReviewRepository CustomerReviewRepository
        {
            get
            {
                if (customerReviewRepository == null)
                {
                    customerReviewRepository = GetRepository<ICustomerReviewRepository>();
                }
                return customerReviewRepository;
            }
        }

        public IAuthorRepository AuthorRepository
        {
            get
            {
                if (authorRepository == null)
                {
                    authorRepository = GetRepository<IAuthorRepository>();
                }
                return authorRepository;
            }
        }

        public ISellerOffersRepository SellerOffersRepository
        {
            get
            {
                if (sellerOffersRepository == null)
                {
                    sellerOffersRepository = GetRepository<ISellerOffersRepository>();
                }
                return sellerOffersRepository;
            }
        }

        public ETLConfiguration Configuration 
        {
            get 
            {
                var configs = innerContext.GetTable<ETLConfiguration>();
                var config = configs.FirstOrDefault();
                if (config == null)
                {
                    config = new ETLConfiguration();
                    configs.InsertOnSubmit(config);
                }
                return config;
            }
        }

        public IDbTransaction Transaction
        {
            get { return innerContext.Transaction; }
        }

        public IBrowseNodeRepository BrowseNodeRepository
        {
            get
            {
                if (browseNodeRepository == null)
                {
                    browseNodeRepository = GetRepository<IBrowseNodeRepository>();
                }
                return browseNodeRepository;
            }
        }

        private T GetRepository<T>()
        {
            var properties = new Hashtable { { "session", this} };
            return IoCGateway.Resolve<T>(properties);
        }

        public IQuerySource<T> GetQuerySource<T>() where T : class
        {
            return new TableQuerySource<T>(innerContext.GetTable<T>());
        }

        public void Fetch<T>(Expression<Func<T, Object>> fetchObject)
        {
            var options = new DataLoadOptions();
            options.LoadWith(fetchObject);
            innerContext.LoadOptions = options;
        }

        public void RemoveFetchOptions()
        {
            innerContext.LoadOptions = null;
        }

        public void Dispose()
        {
            SubmitChanges();
            innerContext.Dispose();
        }

        public bool Has<TEntity>(TEntity item) where TEntity:class
        {
            var trackedObject = innerContext.GetTable<TEntity>().GetOriginalEntityState(item);
            return trackedObject != null;
        }

        
    }
}